package com.chujian.framework.util.mybatis;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.util.ReflectUtil;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.chujian.framework.core.tsdata.TsDataConst;
import com.chujian.framework.mybatis.tenant.LogicIgnoreAcTableHolder;
import com.chujian.framework.mybatis.tenant.WithNoAcExecutor;
import com.chujian.framework.util.mybatis.wrapper.*;

import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 *
 */
public class MybatisUs {

    public static final String FIELD_QUERY = TsDataConst.COMMON_QUERY_FIELD;

    public static final String ORDER_ASC = "asc";
    public static final String ORDER_DESC = "desc";

    // 实体类主键缓存
    public static final ConcurrentHashMap<Class<?>, String> entityClassPkColCache = new ConcurrentHashMap<>();

    // 通用列条件构建器
    private static final List<IFieldWrapperBuilder> FIELD_WRAPPER_BUILDERS =
            ListUtil.toList(
                    new FieldIgnoreWrapperBuilder(),
                    new FieldNumberWrapperBuilder(),
                    new FieldStringWrapperBuilder(),
                    new FieldDateWrapperBuilder(),
                    new FieldBooleanWrapperBuilder()
            );
    // 操作符条件构建器
    private static final List<IOperateWrapperBuilder> OPERATE_WRAPPER_BUILDERS =
            ListUtil.toList(
                    new OperateBetweenWrapperBuilder(),
                    new OperateInWrapperBuilder()
            );


    /**
     * 将数据对象转化为 QueryWrapper
     * <p>
     * 转化规则：
     * 数字型： eq
     * 字符型： like %%
     *
     * @param data 条件对象
     * @param <T>  泛型
     * @return QueryWrapper
     */
    @SuppressWarnings("unchecked")
    public static <T> QueryWrapper<T> toSmartQueryWrapper(T data) {

        Class<?> clazz = data.getClass();
        QueryWrapper<T> wrapper = new QueryWrapper<>();

        // 通用查询
        for (Field field : ReflectUtil.getFields(clazz)) {
            // field / fieldType / colName / 操作符
            Object value = ReflectUtil.getFieldValue(data, field);
            WrapperUnit unit = new WrapperUnit(wrapper, field, value);
            for (IFieldWrapperBuilder builder : FIELD_WRAPPER_BUILDERS) {
                if (builder.build(unit)) break;
            }
        }

        // 综合查询，高级查询，q
        Field qField = ReflectUtil.getField(clazz, FIELD_QUERY);
        if (null != qField) {
            Map<String, String> map = (Map<String, String>) ReflectUtil.getFieldValue(data, FIELD_QUERY);
            map.forEach((reqName, val) -> {
                String[] parts = reqName.split("__");
                String fieldName = parts[0];
                String operate = parts[1];
                Field field = ReflectUtil.getField(clazz, fieldName);
                WrapperUnit unit = new WrapperUnit(wrapper, field, val, operate);
                for (IOperateWrapperBuilder builder : OPERATE_WRAPPER_BUILDERS) {
                    if (builder.build(unit)) break;
                }
            });
        }

        return wrapper;
    }

    // TODO cache
    public static String getMyBatisColName(Field field) {
        TableId tableId = field.getAnnotation(TableId.class);
        TableField tableField = field.getAnnotation(TableField.class);
        if (null != tableId) {
            return tableId.value();
        }
        if (null != tableField) {
            return tableField.value();
        }
        return null;
    }

    /**
     * 获取实体类的主键，使用了缓存
     *
     * @param entityClass entityClass
     * @return pkCol
     */
    public static String getEntityPkColName(Class<?> entityClass) {
        String col = entityClassPkColCache.get(entityClass);
        if (null != col) {
            return col;
        }
        synchronized (entityClassPkColCache) {
            List<Field> fields = Arrays.asList(ReflectUtil.getFields(entityClass));
            Field tableId = CollUtil.findOne(fields, field -> null != field.getAnnotation(TableId.class));
            col = null != tableId ? tableId.getAnnotation(TableId.class).value() : "";
            entityClassPkColCache.put(entityClass, col);
        }
        return col;
    }

    public static String getEntityTableName(Class<?> entityClass) {
        TableName tableName = entityClass.getAnnotation(TableName.class);
        return tableName.value();
    }

    /**
     * 排除AC条件执行
     *
     * @param tableNames
     * @param reason
     * @param executor
     */
    public static void execWithNoAc(List<String> tableNames, String reason, WithNoAcExecutor executor) {
        try {
            for (String tableName : tableNames) {
                LogicIgnoreAcTableHolder.addIgnoreAcTable(tableName, reason);
            }
            executor.exec();
        } finally {
            for (String tableName : tableNames) {
                LogicIgnoreAcTableHolder.removeIgnoreAcTable(tableName);
            }
        }
    }


}
